package com.gooluke.spring.web;

import com.alibaba.fastjson2.JSONObject;
import com.gooluke.spring.BeanPostProcessor;
import com.gooluke.spring.annotation.*;
import com.gooluke.spring.util.AnnotationUtils;
import com.gooluke.spring.util.ParamResolveUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @author gooluke
 * description
 * datetime 2025-04-14 23:23
 */
@Component
public class MyDispatcherServlet extends HttpServlet implements BeanPostProcessor {

    private Map<String, WebHandler> handlerMap = new HashMap<>();

    @Value("${server.servlet.context-path:}")
    private String contextPath;


    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        WebHandler handler = findHandler(req);
        if (handler == null) {
            resp.setContentType("text/html;charset=UTF-8");
            resp.getWriter().write("<h1>404 Error!!! 没有找到对应路径</h1> <br> " + req.getRequestURL().toString());
            return;
        }
        try {
            Object bean = handler.getControllerBean();
            Object[] args = resolveParams(req, handler.getMethod());
            Object result = handler.getMethod().invoke(bean, args);
            switch (handler.getResultType()) {
                case HTML:
                    resp.setContentType("text/html;charset=UTF-8");
                    resp.getWriter().write("<h1>返回html</h1> <br> " + result.toString());
                    break;
                case JSON:
                    resp.setContentType("application/json;charset=UTF-8");
                    resp.getWriter().write(JSONObject.toJSONString(result));
                    break;
                case LOCAL:
                    break;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Object[] resolveParams(HttpServletRequest req, Method method) {
        Parameter[] parameters = method.getParameters();
        Object[] args = new Object[parameters.length];
        for (int i = 0; i < parameters.length; i++) {
            args[i] = resolveParameter(req, parameters[i]);
        }
        return args;
    }

    private Object resolveParameter(HttpServletRequest req, Parameter parameter) {
        RequestBody requestBodyAnnotation = parameter.getAnnotation(RequestBody.class);
        if (requestBodyAnnotation != null) {
            //把请求体里的内容解析为对象
            return ParamResolveUtil.resolveRequestBody(req, parameter);
        }
        RequestParam annotation = parameter.getAnnotation(RequestParam.class);
        String paramName = annotation != null ? annotation.name() : null;
        if (paramName == null) {
            return null;
        }
        String value = req.getParameter(paramName);
        if (annotation.required() && value == null) {
            throw new RuntimeException("param can not be null or set required false : " + paramName);
        }
        return ParamResolveUtil.resolveParam(parameter.getType(), paramName, value);
    }

    private WebHandler findHandler(HttpServletRequest req) {
        return handlerMap.get(req.getRequestURI());
    }

    @Override
    public Object afterInitializeBean(Object bean, String beanName) {
        if (AnnotationUtils.hasAnnotation(bean.getClass(), Controller.class)) {
            RequestMapping annotation = bean.getClass().getAnnotation(RequestMapping.class);
            String classUrl = annotation != null ? annotation.value() : "";
            Arrays.stream(bean.getClass().getDeclaredMethods())
                    .filter(method -> method.isAnnotationPresent(RequestMapping.class))
                    .forEach(method -> {
                        RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);
                        String methodUrl = methodAnnotation.value();
                        String key = classUrl + methodUrl;
                        if (handlerMap.containsKey(key)) {
                            throw new RuntimeException("controller定义重复：" + key);
                        }
                        handlerMap.put(contextPath + key, new WebHandler(bean, method));
                    });
        }
        return bean;
    }
}
